/*
    Modified from The Loki Library
    Modified by : 2013 darkcat

    Copyright (c) 2001 by Andrei Alexandrescu

    This code accompanies the book:
    Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design 
        Patterns Applied". Copyright (c) 2001. Addison-Wesley.
    Permission to use, copy, modify, distribute and sell this software for any 
        purpose is hereby granted without fee, provided that the above copyright 
        notice appear in all copies and that both that copyright notice and this 
        permission notice appear in supporting documentation.
    The author or Addison-Welsey Longman make no representations about the 
        suitability of this software for any purpose. It is provided "as is" 
        without express or implied warranty.
*/

#pragma once

// nx...
#include "config/general.h"
// nx_select
#include "type/typetools.h"
// nx_is_supsub
#include "type/typerelation.h"
// Preprocessor
#include "macro/preprocessor.h"

//////////////////////////////////////////////////////////////////////////
NX_BEG namespace type {
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
// define type list
//////////////////////////////////////////////////////////////////////////

template <typename T, typename U>
struct List
{
    typedef T head_t;
    typedef U tail_t;
};

//////////////////////////////////////////////////////////////////////////
// define type list operations
//////////////////////////////////////////////////////////////////////////

/*
    Get the types number of a List
*/

template <class List_>
struct Len;

template <>
struct Len<null_t>
{
    enum { val = 0 };
};

template <typename T, typename U, 
template <typename, typename> class L_>
struct Len<L_<T, U> >
{
    enum { val = 1 + Len<U>::val };
};

#define nx_types_len(...) nx::type::Len<__VA_ARGS__>::val

/*
    Get a Type from a List with the index
*/

template <class List_, int I>
struct At;

template <typename T, typename U, 
template <typename, typename> class L_>
struct At<L_<T, U>, 0>
{
    typedef T ret;
};

template <typename T, typename U, int I, 
template <typename, typename> class L_>
struct At<L_<T, U>, I>
{
    typedef typename At<U, I - 1>::ret ret;
};

#define nx_types_at(...) nx::type::At<__VA_ARGS__>::ret

/*
    Find one Type of a List
*/

template <class List_, typename T>
struct Find;

template <typename Type_>
struct Find<null_t, Type_>
{
    enum { val = -1 };
};

template <typename U, typename Type_, 
template <typename, typename> class L_>
struct Find<L_<Type_, U>, Type_>
{
    enum { val = 0 };
};

template <typename T, typename U, typename Type_, 
template <typename, typename> class L_>
struct Find<L_<T, U>, Type_>
{
private:
    enum { tmp = Find<U, Type_>::val };
public:
    enum { val = (tmp == -1 ? -1 : 1 + tmp) };
};

#define nx_types_find(...)  nx::type::Find<__VA_ARGS__>::val
#define nx_types_exist(...) (nx_types_find(__VA_ARGS__) != -1)

/*
    Add one Type to a List
*/

template <class List_, typename T>
struct Add;

template <>
struct Add<null_t, null_t>
{
    typedef null_t ret;
};

template <typename T, typename U, 
template <typename, typename> class L_>
struct Add<null_t, L_<T, U> >
{
    typedef L_<T, U> ret;
};

template <
template <typename, typename> class L_>
struct Add<L_<null_t, null_t>, null_t>
{
    typedef null_t ret;
};

template <typename T, 
template <typename, typename> class L_>
struct Add<L_<null_t, null_t>, T>
{
    typedef L_<T, null_t> ret;
};

template <typename T, typename Type_, 
template <typename, typename> class L_>
struct Add<L_<T, null_t>, Type_>
{
    typedef L_<T, typename Add<L_<null_t, null_t>, Type_>::ret> ret;
};

template <typename T, typename U, typename Type_, 
template <typename, typename> class L_>
struct Add<L_<T, U>, Type_>
{
    typedef L_<T, typename Add<U, Type_>::ret> ret;
};

#define nx_types_add(...) nx::type::Add<__VA_ARGS__>::ret

/*
    Delete a Type from a List
*/

template <class List_, typename T>
struct Del;

template <typename T>
struct Del<null_t, T>
{
    typedef null_t ret;
};

template <typename Type_, typename U, 
template <typename, typename> class L_>
struct Del<L_<Type_, U>, Type_>
{
    typedef U ret;
};

template <typename T, typename U, typename Type_, 
template <typename, typename> class L_>
struct Del<L_<T, U>, Type_>
{
    typedef L_<T, typename Del<U, Type_>::ret> ret;
};

#define nx_types_del(...) nx::type::Del<__VA_ARGS__>::ret

/*
    Erase all this Type from a List
*/

template <class List_, typename T>
struct Erase;

template <typename T>
struct Erase<null_t, T>
{
    typedef null_t ret;
};

template <typename Type_, typename U, 
template <typename, typename> class L_>
struct Erase<L_<Type_, U>, Type_>
{
    typedef typename Erase<U, Type_>::ret ret;
};

template <typename T, typename U, typename Type_, 
template <typename, typename> class L_>
struct Erase<L_<T, U>, Type_>
{
    typedef L_<T, typename Erase<U, Type_>::ret> ret;
};

#define nx_types_erase(...) nx::type::Erase<__VA_ARGS__>::ret

/*
    Remove duplicate types, make a List more compact
*/

template <class List_>
struct Zip;

template <>
struct Zip<null_t>
{
    typedef null_t ret;
};

template <class T, class U, 
template <typename, typename> class L_>
struct Zip<L_<T, U> >
{
private:
    typedef typename Zip<U>::ret L1;
    typedef typename Del<L1, T>::ret L2;
public:
    typedef L_<T, L2> ret;
};

#define nx_types_zip(...) nx::type::Zip<__VA_ARGS__>::ret

/*
    Replace a Type to another Type
*/

template <class List_, typename T1, typename T2>
struct Replace;

template <typename T1, typename T2>
struct Replace<null_t, T1 , T2>
{
    typedef null_t ret;
};

template <typename T1, typename U, typename T2, 
template <typename, typename> class L_>
struct Replace<L_<T1, U>, T1 , T2>
{
    typedef L_<T2, U> ret;
};

template <typename T, typename U, typename T1, typename T2, 
template <typename, typename> class L_>
struct Replace<L_<T, U>, T1 , T2>
{
    typedef L_<T, typename Replace<U, T1 , T2>::ret> ret;
};

#define nx_types_replace(...) nx::type::Replace<__VA_ARGS__>::ret

/*
    Reverse the List
*/

template <class List_>
struct Reverse;

template <>
struct Reverse<null_t>
{
    typedef null_t ret;
};

template <typename T, typename U, 
template <typename, typename> class L_>
struct Reverse<L_<T, U> >
{
    typedef typename Add<typename Reverse<U>::ret, T>::ret ret;
};

#define nx_types_rev(...) nx::type::Reverse<__VA_ARGS__>::ret

/*
    Get the most derived type of a List
*/

template <class List_, typename T>
struct MostDerived;

template <class T>
struct MostDerived<null_t, T>
{
    typedef T ret;
};

template <typename T, typename U, typename Type_, 
template <typename, typename> class L_>
struct MostDerived<L_<T, U>, Type_>
{
private:
    typedef typename MostDerived<U, Type_>::ret Candidate;
public:
    typedef typename nx_select(nx_is_supsub(Candidate, T), T, Candidate) ret;
};

/*
    Arranges the types in a typelist so that the most derived types appear first
*/

template <class List_>
struct Sort;

template <>
struct Sort<null_t>
{
    typedef null_t ret;
};

template <typename T, typename U, 
template <typename, typename> class L_>
struct Sort<L_<T, U> >
{
private:
    typedef typename MostDerived<U, T>::ret                 TheMostDerived;
    typedef typename nx_types_replace(U, TheMostDerived, T) Replaced;
    typedef typename Sort<Replaced>::ret                    Sorted;
public:
    typedef L_<TheMostDerived, Sorted> ret;
};

#define nx_types_sort(...) nx::type::Sort<__VA_ARGS__>::ret

/*
    Join types
*/

template <typename T, typename U>
struct Join
{
    typedef List<T, List<U, null_t> > ret;
};

template <>
struct Join<null_t, null_t>
{
    typedef null_t ret;
};

template <typename T>
struct Join<T, null_t>
{
    typedef List<T, null_t> ret;
};

template <typename U>
struct Join<null_t, U>
{
    typedef List<U, null_t> ret;
};

template <typename T, typename U, 
template <typename, typename> class L_>
struct Join<L_<T, U>, null_t>
{
    typedef L_<T, U> ret;
};

template <typename T, typename U, typename Type_, 
template <typename, typename> class L_>
struct Join<L_<T, U>, Type_>
{
    typedef typename Add<L_<T, U>, Type_>::ret ret;
};

template <typename T, typename U, 
template <typename, typename> class L_>
struct Join<null_t, L_<T, U> >
{
    typedef typename Add<null_t, L_<T, U> >::ret ret;
};

template <typename T, typename U, typename Type_, 
template <typename, typename> class L_>
struct Join<Type_, L_<T, U> >
{
    typedef L_<Type_, L_<T, U> > ret;
};

template <typename T1, typename U1, typename T2, typename U2, 
template <typename, typename> class L1_, 
template <typename, typename> class L2_>
struct Join<L1_<T1, U1>, L2_<T2, U2> >
{
    typedef typename Join<L1_<T1, U1>, T2>::ret appended;
    typedef typename Join<appended, U2>::ret ret;
};

#define nx_types_join(...) nx::type::Join<__VA_ARGS__>::ret

/*
    Make types to a Sequence
*/

template <NX_PP_TYPE_MAX(typename T, = null_t)>
struct Seq
{
private:
    typedef typename Seq<NX_ARGS_L1(NX_PP_TYPE_MAX(T))>::ret tail_t;
public:
    typedef typename Join<T1, tail_t>::ret ret;
};

template <>
struct Seq<>
{
    typedef null_t ret;
};

#define nx_types_seq(...)   nx::type::Seq<__VA_ARGS__>::ret
#define nx_types(...)       nx_types_seq(__VA_ARGS__)

/*
    Macro for make TypeList directly

    NX_TYPES(T1, T2, T3, T4)
    -->
    nx::type::List<T1, nx::type::List<T2, nx::type::List<T3, nx::type::List<T4, nx::type::null_t> > > >
*/

#define NX_TYPES_E(args, i, nul) nx::type::List<NX_ARGS_F1(args), nx::type::null_t>
#define NX_TYPES_F(args, i, nxt) nx::type::List<NX_ARGS_F1(args), nxt >
#define NX_TYPES_I(args)         NX_ARGS_L1(args)

#define NX_TYPES(...) \
    NX_LOOP(NX_CONTER(__VA_ARGS__), NX_TYPES_F, NX_TYPES_I, NX_TYPES_E, , __VA_ARGS__)

/*
    Helper for special list algorithms
*/

#define NX_SPECIAL_TYPES_0(name, spl_class, ...) \
    template <NX_PP_ITERA_ARGS(T, __VA_ARGS__)> \
    struct name<spl_class<NX_PP_TYPE(NX_CONTER(__VA_ARGS__), T)> > \
         : name<typename spl_class<NX_PP_TYPE(NX_CONTER(__VA_ARGS__), T)>::base_t> \
    {};

#define NX_SPECIAL_TYPES_1(name, typename1, spl_class, ...) \
    template <NX_PP_ITERA_ARGS(T, __VA_ARGS__), typename1 Type_> \
    struct name<spl_class<NX_PP_TYPE(NX_CONTER(__VA_ARGS__), T)>, Type_> \
         : name<typename spl_class<NX_PP_TYPE(NX_CONTER(__VA_ARGS__), T)>::base_t, Type_> \
    {};

#define NX_SPECIAL_TYPES_2(name, typename1, typename2, spl_class, ...) \
    template <NX_PP_ITERA_ARGS(T, __VA_ARGS__), typename1 Type1_, typename2 Type2_> \
    struct name<spl_class<NX_PP_TYPE(NX_CONTER(__VA_ARGS__), T)>, Type1_, Type2_> \
         : name<typename spl_class<NX_PP_TYPE(NX_CONTER(__VA_ARGS__), T)>::base_t, Type1_, Type2_> \
    {};

#define NX_SPECIAL_TYPES_R(name, typename1, spl_class, ...) \
    template <typename1 Type_, NX_PP_ITERA_ARGS(T, __VA_ARGS__)> \
    struct name<Type_, spl_class<NX_PP_TYPE(NX_CONTER(__VA_ARGS__), T)> > \
         : name<Type_, typename spl_class<NX_PP_TYPE(NX_CONTER(__VA_ARGS__), T)>::base_t> \
    {};

#define NX_SPECIAL_TYPES_D(name, spl_class, ...) \
    template <NX_PP_ITERA_ARGS(T, __VA_ARGS__), \
              NX_PP_ITERA_ARGS(U, __VA_ARGS__)> \
    struct name<spl_class<NX_PP_TYPE(NX_CONTER(__VA_ARGS__), T)>, \
                spl_class<NX_PP_TYPE(NX_CONTER(__VA_ARGS__), U)> > \
         : name<typename spl_class<NX_PP_TYPE(NX_CONTER(__VA_ARGS__), T)>::base_t, \
                typename spl_class<NX_PP_TYPE(NX_CONTER(__VA_ARGS__), U)>::base_t> \
    {};

#define NX_SPECIAL_TYPES(spl_class, ...) \
namespace type \
{ \
    NX_SPECIAL_TYPES_0(Len                                , spl_class, __VA_ARGS__) \
    NX_SPECIAL_TYPES_1(At         , int                   , spl_class, __VA_ARGS__) \
    NX_SPECIAL_TYPES_1(Find       , typename              , spl_class, __VA_ARGS__) \
    NX_SPECIAL_TYPES_1(Add        , typename              , spl_class, __VA_ARGS__) \
    NX_SPECIAL_TYPES_1(Del        , typename              , spl_class, __VA_ARGS__) \
    NX_SPECIAL_TYPES_1(Erase      , typename              , spl_class, __VA_ARGS__) \
    NX_SPECIAL_TYPES_0(Zip                                , spl_class, __VA_ARGS__) \
    NX_SPECIAL_TYPES_2(Replace    , typename  , typename  , spl_class, __VA_ARGS__) \
    NX_SPECIAL_TYPES_0(Reverse                            , spl_class, __VA_ARGS__) \
    NX_SPECIAL_TYPES_1(MostDerived, typename              , spl_class, __VA_ARGS__) \
    NX_SPECIAL_TYPES_0(Sort                               , spl_class, __VA_ARGS__) \
    NX_SPECIAL_TYPES_1(Join       , typename              , spl_class, __VA_ARGS__) \
    NX_SPECIAL_TYPES_R(Join       , typename              , spl_class, __VA_ARGS__) \
    NX_SPECIAL_TYPES_D(Join                               , spl_class, __VA_ARGS__) \
}

//////////////////////////////////////////////////////////////////////////
} NX_END
//////////////////////////////////////////////////////////////////////////
